package Thread;

import jdk.nashorn.internal.ir.Block;

//t1线程启动，就会调用wait进入阻塞等待
//t2线程启动，就会先sleep，sleep时间之后再进行notify唤醒t1
public class TreadDemo21 {
    public static void main(String[] args) {
        Object locker=new Object();
        Thread t1=new Thread(()->{
            System.out.println("t1 wait之前");
            synchronized (locker){//wait必须放到synchronized里面，因为要释放锁，前提是加上锁
                try {
                    locker.wait();//wait和sleep,join一类的，都可能会被interrupt提前唤醒因此要try.catch
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("t1 wait之后");
            }
        });
        Thread t2=new Thread(()->{
            try {
                Thread.sleep(5000);
                //注意sleep要写到synchronized外面，否则，由于t1 t2执行顺序不确定，就可能t2先拿到锁
                //此时t1就没有执行到wait，t2就先notify了，结果不符合预期(这里需要确保代码现在执行wait后执notify)
                synchronized (locker){
                    //notify其实可以不用放到synchronized里面，不需要先加锁的，但是java中特别约定要放到synchronized里面
                    System.out.println("t2 notify之前");
                    locker.notify();
                    System.out.println("t2 notify之后");
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }


        });
        t1.start();
        t2.start();
    }
}
//上述代码的执行过程
//1)T1执行起来之后就会立即拿到锁，并且打印t1 wait之前，并且进入wait方法（释放锁+阻塞等待）
//2)t2执行起来之后，先进行sleep操作，由于sleep的时间对计算机来说挺长的，就可以让t1先拿到锁
//3)t2 sleep结束之后，由于t1是wait状态，锁是释放的，t2就能拿到锁
//接下来打印t2 notify之前,执行notify操作，这个操作就能唤醒t1（此时t1就从WAITING状态恢复回来了
//4)但是由于t2此时还没有释放锁，t1 WAITING恢复之后，尝试获取锁就可能出现一个小小的阻塞，这个阻塞是锁竞争引起的
//（肉眼很难看到BLOCKED状态，这个状态的变换是非常快的）
//5)执行完t2 notify之后，释放锁，t2执行完毕，t1的wait就可以获取到锁了，继续打印t1 wait之后


//必须两个对象一致才能唤醒
//如果两个wait是不同的对象调用的，此时notify使用的是哪个对象，就唤醒哪个对象的wait
//如果两个wait是同一个对象调用的，随机唤醒其中一个
//notifyAll(较少使用)---唤醒这个对象上所有等待的线程
//假设有很多个线程，都使用同一个对象wait，针对这个对象notifyAll此时就会全都唤醒
//但是这些线程在wait返回的时候，要重新获取锁，就会因为锁的竞争，使这些线程实际上是一个一个串行执行的（谁先拿到锁也是不确定的）
